package drds.plus.executor.cursor.cursor.impl.join;

import drds.plus.executor.cursor.cursor.ISortingCursor;
import drds.plus.executor.cursor.cursor.impl.SortingCursor;
import drds.plus.executor.cursor.cursor_metadata.CursorMetaData;
import drds.plus.executor.cursor.cursor_metadata.CursorMetaDataImpl;
import drds.plus.executor.row_values.JoinRowValues;
import drds.plus.executor.row_values.RowValues;
import drds.plus.sql_process.abstract_syntax_tree.configuration.ColumnMetaData;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Join;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.order_by.OrderBy;

import java.util.ArrayList;
import java.util.List;

/**
 * join的结果
 */
public class JoinCursor extends SortingCursor {

    protected ISortingCursor leftCursor;
    protected ISortingCursor rightCursor;
    /**
     * 因为左右cursor要拼到一起，所以右值必须加一个偏移量
     */
    protected Integer rightCursorOffset;
    protected CursorMetaData cursorMetaData = null;
    //protected Comparator<RowValues> rowDataComparator;
    protected List<Item> leftJoinItemList;
    protected List<Item> rightJoinItemList;
    /**
     * <pre>
     * 见 Join
     * leftOuterJoin:
     *      leftOuter=true && rightOuter=false
     * rightOuterJoin:
     *      leftOuter=false && rightOuter=true
     * innerJoin:
     *      leftOuter=false && rightOuter=false
     * outerJoin:
     *      leftOuter=true && rightOuter=true
     * </pre>
     */
    protected boolean leftOutJoin = false;
    protected boolean rightOutJoin = false;
    private boolean schemaInited = false;
    private List<ColumnMetaData> columnMetaDataList;

    public JoinCursor(ISortingCursor leftCursor, List leftJoinItemList, ISortingCursor rightCursor, List rightJoinItemList) {
        super(null, null, null);
        this.leftCursor = leftCursor;
        this.rightCursor = rightCursor;

        this.leftJoinItemList = leftJoinItemList;
        this.rightJoinItemList = rightJoinItemList;

        schemaInited = false;
    }

    public void setLeftJoin(boolean left) {
        this.leftOutJoin = left;
    }

    public void setRightJoin(boolean right) {
        this.rightOutJoin = right;
    }

    public void setLeftRightJoin(Join join) {
        if (join != null) {
            this.leftOutJoin = join.isLeftOuterJoin();
            this.rightOutJoin = join.isRightOuterJoin();
        } else {
            throw new RuntimeException("Join join is null");
        }
    }

    public boolean isLeftOutJoin() {
        return leftOutJoin;
    }

    public boolean isRightOutJoin() {
        return rightOutJoin;
    }

    protected void build(CursorMetaData leftCursorMetaData, CursorMetaData rightSideCursorMetaData) throws RuntimeException {
        if (schemaInited) {
            return;
        }

        schemaInited = true;
        // 以左面数据顺序，作为排序
        setOrderByList(leftCursor);
        //
        //this.rowDataComparator = RowValueScaners.getComparator(leftSideCursorMetaData, this.leftJoinItemList, rightCursorMetaData, this.rightJoinItemList);
        //
        List<ColumnMetaData> leftCursorMetaDataColumnMetaDataList = leftCursorMetaData.getColumnMetaDataList();
        rightCursorOffset = leftCursorMetaData.getIndexRange();
        List<ColumnMetaData> rightCursorMetaDataColumnMetaDataList = rightSideCursorMetaData.getColumnMetaDataList();
        List<ColumnMetaData> columnMetaDataList = new ArrayList<ColumnMetaData>(leftCursorMetaDataColumnMetaDataList.size() + rightCursorMetaDataColumnMetaDataList.size());
        columnMetaDataList.addAll(leftCursorMetaDataColumnMetaDataList);
        columnMetaDataList.addAll(rightCursorMetaDataColumnMetaDataList);
        //
        List<Integer> indexList = new ArrayList<Integer>(columnMetaDataList.size());
        //
        addIndexToNewIndexes(leftCursorMetaData, leftCursorMetaDataColumnMetaDataList, indexList, 0);
        addIndexToNewIndexes(rightSideCursorMetaData, rightCursorMetaDataColumnMetaDataList, indexList, rightCursorOffset);
        CursorMetaData cursorMetaData = CursorMetaDataImpl.buildCursorMetaData(columnMetaDataList, indexList, (leftCursorMetaData.getIndexRange() + rightSideCursorMetaData.getIndexRange()));
        this.cursorMetaData = cursorMetaData;

    }

    private void addIndexToNewIndexes(CursorMetaData cursorMetaData, List<ColumnMetaData> columnMetaDataList, List<Integer> indexList, int offset) {
        for (ColumnMetaData columnMetaData : columnMetaDataList) {
            Integer index = cursorMetaData.getIndex(columnMetaData.getTableName(), columnMetaData.getColumnName(), columnMetaData.getAlias());
            indexList.add(offset + index);
        }
    }

    protected void build(List<ColumnMetaData> leftColumnMetaDataList, List<ColumnMetaData> rightColumnMetaDataList) throws RuntimeException {
        if (schemaInited) {
            return;
        }
        schemaInited = true;
        // 以左面数据顺序，作为排序
        setOrderByList(leftCursor);
        List<ColumnMetaData> columnMetaDataList = new ArrayList<ColumnMetaData>(leftColumnMetaDataList.size() + rightColumnMetaDataList.size());
        columnMetaDataList.addAll(leftColumnMetaDataList);
        columnMetaDataList.addAll(rightColumnMetaDataList);
        CursorMetaData cursorMetaData = CursorMetaDataImpl.buildCursorMetaData(columnMetaDataList);
        rightCursorOffset = leftColumnMetaDataList.size();
        this.cursorMetaData = cursorMetaData;
    }


    public RowValues join(RowValues leftRowData, RowValues rightRowData) throws RuntimeException {
        CursorMetaData leftSideCursorMetaData = null;
        CursorMetaData rightSideCursorMetaData = null;
        if (leftRowData != null) {
            leftSideCursorMetaData = leftRowData.getParentCursorMetaData();
        }
        if (rightRowData != null) {
            rightSideCursorMetaData = rightRowData.getParentCursorMetaData();
        }
        build(leftSideCursorMetaData, rightSideCursorMetaData);

        RowValues joinRowData = new JoinRowValues(rightCursorOffset, leftRowData, rightRowData, cursorMetaData);
        return joinRowData;
    }

    private void setOrderByList(ISortingCursor sortingCursor) throws RuntimeException {
        List<OrderBy> orderByList = sortingCursor.getOrderByList();
        setOrderByList(orderByList);
    }

    public List<RuntimeException> close(List<RuntimeException> exs) {
        if (leftCursor != null) {
            exs = leftCursor.close(exs);
        }
        if (rightCursor != null) {
            exs = rightCursor.close(exs);
        }

        return exs;
    }

    public List<ColumnMetaData> getColumnMetaDataList() throws RuntimeException {
        if (this.columnMetaDataList != null) {
            return this.columnMetaDataList;
        }

        List<ColumnMetaData> leftCursorColumnMetaDataList = this.leftCursor.getColumnMetaDataList();
        List<ColumnMetaData> rightCursorColumnMetaDataList = this.rightCursor.getColumnMetaDataList();

        columnMetaDataList = new ArrayList(leftCursorColumnMetaDataList.size() + rightCursorColumnMetaDataList.size());
        columnMetaDataList.addAll(leftCursorColumnMetaDataList);
        columnMetaDataList.addAll(rightCursorColumnMetaDataList);

        return columnMetaDataList;

    }
}
